package com.tfswx.ruleengine.compute.util;


import com.tfswx.ruleengine.compute.exception.AppException;
import com.tfswx.ruleengine.compute.gzjy.BaseFn;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

/**
 * @author Ricky
 * @date 2021/1/26
 */
@Slf4j
public class ClassUtil {
    private ClassUtil() {
    }

    /**
     * 根据Class获取项目(包括依赖)中的子类或实现类
     *
     * @param parentCls 父类
     * @return classList
     */
    public static <T> List<Class<T>> getClassesByParentFromAll(@NonNull Class<T> parentCls, String... pks) {
        List<Class<T>> classes = new ArrayList<>();
        List<Class<?>> allClasses = getAllClassesFromAll(pks);
        for (Class<?> c : allClasses) {
            if (parentCls.isAssignableFrom(c) && !parentCls.equals(c)) {
                classes.add((Class<T>) c);
            }
        }
        return classes;
    }

    /**
     * 获取有指定注解的class(包括依赖)
     *
     * @param annotationCls 指定注解class
     * @param pks           包名
     * @return classList
     */
    public static <T extends Annotation> List<Class<?>> getClassesByAnnotationFromAll(Class<T> annotationCls, String... pks) {
        List<Class<?>> classes = new ArrayList<>();
        List<Class<?>> allClasses = getAllClassesFromAll(pks);
        for (Class<?> clazz : allClasses) {
            //判断是否有指定注解
            Annotation annotation = clazz.getAnnotation(annotationCls);
            if (annotation != null) {
                classes.add(clazz);
            }
        }
        return classes;
    }

    /**
     * 获取pks下所有class
     *
     * @param pks 包路径
     * @return 所有class
     */
    private static List<Class<?>> getAllClassesFromAll(String... pks) {
        List<Class<?>> classes = new ArrayList<>();

        //spring工具类，可以获取指定路径下的全部类
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

        List<Resource> resourceList = new ArrayList<>();
        if (pks.length == 0) {
            pks = new String[]{""};
        }
        for (String pk : pks) {
            Resource[] resources = getResources(resourcePatternResolver, pk);
            resourceList.addAll(Arrays.asList(resources));
        }
        //MetadataReader 的工厂类
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
        MetadataReader metadataReader;
        for (Resource resource : resourceList) {
            if (!resource.isReadable()) {
                continue;
            }
            try {
                metadataReader = metadataReaderFactory.getMetadataReader(resource);
                ClassMetadata classMetadata = metadataReader.getClassMetadata();
                String className = classMetadata.getClassName();
                Class<?> aClass = Class.forName(className);
                classes.add(aClass);
            } catch (Throwable e) {
                log.warn("错误：{}", e.getMessage());
            }
        }
        return classes;
    }

    private static Resource[] getResources(ResourcePatternResolver resourcePatternResolver, String pk) {
        String resourcePattern = "/**/*.class";
        String prefix = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
        String pkPath = "";
        if (StringUtils.isNotBlank(pk)) {
            pkPath = ClassUtils.convertClassNameToResourcePath(pk);
        } else {
            log.warn("建议不要在根目录中查找，该操作将会很耗时");
        }
        try {
            return resourcePatternResolver.getResources(prefix + pkPath + resourcePattern);
        } catch (IOException e) {
            log.warn("获取class失败：{}", e.getMessage());
            return new Resource[0];
        }
    }

    public static List<BaseFn> getFnListByJar(File jarFile) {
        if (!jarFile.exists()) {
            throw new AppException(jarFile.getPath() + " 不存在");
        }
        List<BaseFn> fnList = new ArrayList<>();
        try (URLClassLoader classLoader = new URLClassLoader(new URL[]{jarFile.toURI().toURL()}, Thread.currentThread().getContextClassLoader())) {
            //通过jarFile和JarEntry得到所有的类
            JarFile jar = new JarFile(jarFile);
            //返回zip文件条目的枚举
            Enumeration<JarEntry> enumFiles = jar.entries();
            JarEntry entry;

            //测试此枚举是否包含更多的元素
            while (enumFiles.hasMoreElements()) {
                entry = enumFiles.nextElement();
                if (entry.getName().contains("META-INF")) {
                    continue;
                }
                String classFullName = entry.getName();
                if (!classFullName.endsWith(".class")) {
                    continue;
                }

                String className = classFullName.substring(0, classFullName.length() - 6).replace("/", ".");
                Class<?> myclass = classLoader.loadClass(className);
                Class<?> parentCls = BaseFn.class;
                if (!parentCls.isAssignableFrom(myclass) || parentCls.equals(myclass)) {
                    continue;
                }
                Object o = myclass.newInstance();
                fnList.add((BaseFn) o);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            throw new AppException("类找不到");
        } catch (InstantiationException e) {
            throw new AppException("构造方法执行异常");
        } catch (IllegalAccessException e) {
            throw new AppException("构造方法无法访问");
        }
        return fnList.stream().filter(BaseFn::isExport).collect(Collectors.toList());
    }

    /**
     * 加载jar包
     *
     * @param jarFile jar包文件
     * @return class
     */
    public static void loadClass(File jarFile) {
        if (!jarFile.exists()) {
            throw new AppException(jarFile.getPath() + " 不存在");
        }
        try {
            URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();

            Method add = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            boolean accessible = add.isAccessible();
            add.setAccessible(true);
            add.invoke(classLoader, jarFile.toURI().toURL());
            add.setAccessible(accessible);
            log.info("{}加载完成", jarFile.getPath());
        } catch (InvocationTargetException e) {
            throw new AppException("addURL方法执行失败");
        } catch (NoSuchMethodException e) {
            throw new AppException("未找到方法：addURL");
        } catch (MalformedURLException e) {
            throw new AppException("转换URL失败");
        } catch (IllegalAccessException e) {
            throw new AppException("addURL方法无法访问");
        }
    }

    public static void main(String[] args) {
        //        List<Class<Enum>> allAssignedClass = getClassesByParentFromAll(Enum.class);
        //        System.out.println(allAssignedClass);

        List<BaseFn> fnListByJar = getFnListByJar(new File("E:\\Users\\Ricky\\Desktop\\rule-fn-1.0.jar"));
        for (BaseFn baseFn : fnListByJar) {
            System.out.println("name=" + baseFn.getName() + ",名字=" + baseFn.getZwmc());
        }
    }
}
